package org.dromara.common.encrypt.filter;

import cn.hutool.core.io.IoUtil;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.encrypt.utils.EncryptUtils;
import org.springframework.http.MediaType;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;


/**
 * @Author 刘武贵
 * @Date 2024/8/15 22:00
 * @Description 功能描述：解密请求参数工具类
 */
/**
 * DecryptRequestBodyWrapper 类继承自 HttpServletRequestWrapper，用于解密请求的主体内容.
 * 该类的目的是在接收到客户端请求时，如果请求体经过了加密处理，由该类负责对请求体进行解密，
 * 确保后端处理逻辑能够接收到明文的请求数据.
 */
public class DecryptRequestBodyWrapper extends HttpServletRequestWrapper {

    /**
     * 定义一个私有的最终字节数组body，用于存储请求的消息体内容。
     * 该字段在类实例化时不可更改，确保了数据的一致性和安全性。
     */
    private final byte[] body;

    /**
     * 解密请求体包装器构造函数
     * 本构造函数接收一个HttpServletRequest对象和两个字符串参数：私钥和头标志
     * 它首先从请求头中获取RSA加密的AES密码，然后使用提供的私钥解密AES密码
     * 接着，使用解密的AES密码对请求体进行解密
     *
     * @param request  原始HTTP请求对象
     * @param privateKey  用于解密AES密码的私钥
     * @param headerFlag  用于从请求头中获取加密AES密码的头标志
     * @throws IOException 如果在读取请求体或其他IO操作中发生错误
     */
    public DecryptRequestBodyWrapper(HttpServletRequest request, String privateKey, String headerFlag) throws IOException {
        super(request);
        // 获取 AES 密码 采用 RSA 加密
        // 从请求头中获取RSA加密的AES密码
        String headerRsa = request.getHeader(headerFlag);
        // 使用私钥解密RSA加密的AES密码
        String decryptAes = EncryptUtils.decryptByRsa(headerRsa, privateKey);

        // 解密 AES 密码
        // 使用Base64解密AES密码
        String aesPassword = EncryptUtils.decryptByBase64(decryptAes);
        // 设置请求字符编码为UTF-8
        request.setCharacterEncoding(Constants.UTF8);
        // 读取请求体字节数据
        byte[] readBytes = IoUtil.readBytes(request.getInputStream(), false);
        // 将请求体字节数据转换为字符串
        String requestBody = new String(readBytes, StandardCharsets.UTF_8);

        // 解密 body 采用 AES 加密
        // 使用解密的AES密码对请求体进行解密
        String decryptBody = EncryptUtils.decryptByAes(requestBody, aesPassword);
        // 将解密后的请求体转换为字节数组，存储在body变量中
        body = decryptBody.getBytes(StandardCharsets.UTF_8);
    }

    /**
     * 获取BufferedReader对象，用于读取数据
     *
     * @return BufferedReader对象，通过包装InputStreamReader和getInputStream方法实现
     *         该方法通过包装底层输入流为带缓冲的字符输入流，提高了字符读取的效率
     */
    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    /**
     * 获取内容长度
     *
     * @return 内容的字节长度
     */
    @Override
    public int getContentLength() {
        return body.length;
    }

    /**
     * 获取内容的长整型长度
     *
     * @return 内容的字节长度，以长整型形式返回
     */
    @Override
    public long getContentLengthLong() {
        return body.length;
    }

    /**
     * 获取内容类型
     *
     * @return 内容类型为APPLICATION_JSON，表示JSON格式的数据
     */
    @Override
    public String getContentType() {
        return MediaType.APPLICATION_JSON_VALUE;
    }

    /**
     * 获取与当前请求相关的输入流
     * 该方法用于将请求体(body)转换为ServletInputStream对象，以便Servlet能够读取请求的内容
     *
     * @return ServletInputStream对象，允许Servlet读取请求体
     */
    @Override
    public ServletInputStream getInputStream() {
        // 使用请求体(body)创建一个ByteArrayInputStream，为后续的输入流提供数据源
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        // 返回一个自定义的ServletInputStream实现，该实现基于ByteArrayInputStrea
        return new ServletInputStream() {

            /**
             * 读取输入流的下一个字节
             *
             * @return 下一个字节的值，如果到达流的末尾则返回-1
             */
            @Override
            public int read() {
                return bais.read();
            }


            /**
             * 返回输入流中可用字节数的估计值
             *
             * @return 输入流中可用的字节数
             */
            @Override
            public int available() {
                return body.length;
            }


            /**
             * 始终返回false，表示该输入流从未完成
             * 这是因为该实现不支持异步读取
             *
             * @return false，表示该输入流从未完成
             */
            @Override
            public boolean isFinished() {
                return false;
            }


            /**
             * 始终返回false，表示该输入流始终准备就绪
             * 这是因为该实现不支持异步读取
             *
             * @return false，表示该输入流始终准备就绪
             */
            @Override
            public boolean isReady() {
                return false;
            }


            /**
             * 为空实现，因为该输入流不支持异步读取
             *
             * @param readListener 未使用，因为该输入流不支持异步读取
             */
            @Override
            public void setReadListener(ReadListener readListener) {

            }
        };
    }
}
